package com.app.bw;

import java.io.File;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;

import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/*
 * Copyright 2001-2005 The Apache Software Foundation.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;

/**
 * https://blog.csdn.net/dieaixia5129/article/details/122405756
 * https://www.jb51.net/article/238421.htm
 */
@Mojo(name = "post-compile", defaultPhase = LifecyclePhase.COMPILE)
public class PostCompileMojo extends AbstractMojo {

	// @Parameter(property = "protectPackages", defaultValue = "com.app", required =
	// true)
	// private List<String> protectPackages;

	// =========================compilePath==>D:\work20220906\java\myopen\springboot2\demo20220728\target\classes
//	@Parameter(defaultValue = "${project.compileClasspathElements}", readonly = true, required = true)
//	private List<String> compilePath;

	@Parameter(defaultValue = "1234567890abcdef", property = "key", required = true)
	private String key;

	/**
	 * 输出地址
	 */
	@Parameter(defaultValue = "${project.build.directory}", property = "outputDir", required = true)
	private File outputDirectory;

	public void execute() throws MojoExecutionException {
		if (key.length() < 16) {
			throw new MojoExecutionException("key length must be 16 !!!");
		}
		getLog().info("=========================key==============>" + key);
		getLog().info("=========================outputDirectory==>" + outputDirectory);
//		protectPackages.forEach((pk) -> {
//			Path p = Paths.get(outputDirectory.toString(), "classes", pk.replaceAll("\\.", "\\"+File.separator));
//			getLog().info("=========================PostCompileMojo111==>" + p.toString());
//		});

//		compilePath.forEach((pk) -> {
//			getLog().info("=========================compilePath==>" + pk);
//		});

		doExecute(outputDirectory);

		File f = outputDirectory;

		if (!f.exists()) {
			f.mkdirs();
		}

		File touch = new File(f, "touch.txt");

		FileWriter w = null;
		try {
			w = new FileWriter(touch);

			w.write(LocalDateTime.now().toString());
		} catch (IOException e) {
			throw new MojoExecutionException("Error creating file " + touch, e);
		} finally {
			if (w != null) {
				try {
					w.close();
				} catch (IOException e) {
					// ignore
				}
			}
		}

	}

	/**
	 * 文件递归查找调用 并且加密
	 * 
	 * @author xuejx
	 * @param root
	 */
	private void doExecute(File f) {
		if (f.isDirectory()) {
			for (File file : f.listFiles()) {
				doExecute(file);
			}
		}
		FileOutputStream fos = null;
		byte[] content = null;
		try {
			if (f.getName().endsWith(".class")) {
				content = Files.readAllBytes(f.toPath());
				if (isClass(content)) {// 防止重复加密
					getLog().info("=========================【encrypt】:" + f.getName());
					content = encrypt(key.getBytes(), content);
				} else {
					getLog().info("=========================【repeat encrypt】:" + f.getName());
				}
			}
//			else if (f.isFile()) {// 文件没有必要拷贝
//				//不知道文件没有编译到路径中
//				System.out.println("【no encrypt】:" + f.getName());
//				content = Files.readAllBytes(f.toPath());
//			}
			if (null != content) {
				fos = new FileOutputStream(f);
				fos.write(content);
				fos.flush();
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

	private static byte[] encrypt(byte[] key, byte[] input) {
		try {
			// 创建密码对象，需要传入算法/工作模式/填充模式
			Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
			// 根据key的字节内容，"恢复"秘钥对象
			SecretKey keySpec = new SecretKeySpec(key, "AES");
			// 初始化秘钥:设置加密模式ENCRYPT_MODE
			cipher.init(Cipher.ENCRYPT_MODE, keySpec);
			// 根据原始内容(字节),进行加密
			return cipher.doFinal(input);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	// 总共4字节，固定为0xCAFEBABE
	private static final byte[] CLASS_MAGIC = ByteBuffer.allocate(Integer.BYTES).putInt(0xCAFEBABE).array();

	// 判断是否class
	private static boolean isClass(byte[] content) {
		boolean isClass = Arrays.equals(content, 0, 4, CLASS_MAGIC, 0, 4);
		return isClass;
	}

	public static void main(String[] args) {
		String classFile = "C:/Users/xueji/Desktop/Hello.class";
		try {
			byte[] content = Files.readAllBytes(Path.of(classFile));
			boolean isClass = isClass(content);
			System.out.println(isClass);
		} catch (IOException e) {
			e.printStackTrace();
		}

	}

}
